package bsplib;

import bsplib.Debug;
import bsplib.Vec3f;
import flash.display.BitmapData;
import flash.Vector;

class BspFace
{
    public static var showWires : Bool = false;
    public static var smoothRender : Bool = false;
    
    public var numTris : Int;
    public var verts : Vector<BspVertex>;
    public var tris : Vector<BspTriangle>;
    
    public var texture : BitmapData;
    public var lightmap : BitmapData;
    
    public var normal : Vec3f;
    public var dist : Float;
    
    public var color : UInt;

    public function new()
    {
        meshScreenVerts = new Vector<Float>();
        meshScreenUVs = new Vector<Float>();
        meshScreenLMUVs = new Vector<Float>();
    }
    
    private var meshScreenVerts : Vector<Float>;
    private var meshScreenUVs : Vector<Float>;
    private var meshScreenLMUVs : Vector<Float>;
    private var meshScreenIndexes : Vector<Int>;
    
    public function render( rsession : BspRenderSession ) : Void {
        
        if ( meshScreenVerts.length < verts.length * 2 ) meshScreenVerts.length = verts.length * 2;
        if ( meshScreenUVs.length < verts.length * 3 ) meshScreenUVs.length = verts.length * 3;
        if ( meshScreenLMUVs.length < verts.length * 3 ) meshScreenLMUVs.length = verts.length * 3;
        if ( meshScreenIndexes == null ) meshScreenIndexes = new Vector<Int>(3 * tris.length);

        rsession.proj.projectAndNearClip2( verts, tris, meshScreenVerts, meshScreenUVs, meshScreenLMUVs, meshScreenIndexes );

        if ( meshScreenIndexes.length > 0 ) {
            var g = rsession.solidCanvas;

            g.beginBitmapFill(texture,null, true, smoothRender);
            if ( showWires ) g.lineStyle(0, 0xFFFFFF);
            g.drawTriangles(meshScreenVerts, meshScreenIndexes, meshScreenUVs);
            if ( showWires ) g.lineStyle();
            g.endFill();
            
            if ( lightmap != null ) {
                g = rsession.lightCanvas;
                g.beginBitmapFill(lightmap,null, true, smoothRender);
                g.drawTriangles(meshScreenVerts, meshScreenIndexes, meshScreenLMUVs);
                g.endFill();
            }
        }
    }
    
    public function shallowClone() : BspFace {
        var result = new BspFace();
        result.numTris = numTris;

        if ( verts != null ) {
            result.verts = new Vector<BspVertex>(verts.length);
            for ( i in 0...verts.length ) {
                result.verts[i] = verts[i].clone();
            }
        }
        
        if ( tris != null ) {
            result.tris = new Vector<BspTriangle>(tris.length);
            for ( i in 0...tris.length ) {
                var a = new BspTriangle();
                a.v0 = result.verts[tris[i].v0.index];
                a.v1 = result.verts[tris[i].v1.index];
                a.v2 = result.verts[tris[i].v2.index];
                result.tris[i] = a;
            }
        }

        result.normal = normal;
        result.color = color;
        result.texture = texture;
        result.lightmap = lightmap;
        return result;
    }
    
    public function testAsserts() : Void {
        if ( tris != null ) {
            for ( t in tris ) {
                Debug.assert( t.v0 == verts[t.v0.index] );
                Debug.assert( t.v1 == verts[t.v1.index] );
                Debug.assert( t.v2 == verts[t.v2.index] );
            }   
        }
        
    }
    
    public function clip( normal : Vec3f, dist : Float ) : Void {
        testAsserts();
        
        if ( numTris <= 0 ) return;
        var resultVertNum = 0;

        for ( vert in verts ) {
            vert.screenZ = normal.dot( vert.pos ) - dist;
            if ( vert.screenZ >= -0.1 ) {
                vert.index = resultVertNum++;
            }else {
                vert.index = -1;
            }
        }
        
        var resultVerts = new Vector<BspVertex>( resultVertNum );
        
        for ( vert in verts ) {
            if ( vert.index >= 0 ) resultVerts[vert.index] = vert;
        }
        
        var resultTris = new Vector<BspTriangle>();
        
        var idxCursor = 0;
        for ( tri in tris ) {
            var v0 = tri.v0;
            var v1 = tri.v1;
            var v2 = tri.v2;

            var c0 = v0.index >= 0;
            var c1 = v1.index >= 0;
            var c2 = v2.index >= 0;
            
            if ( !c0 && !c1 && !c2 ) continue;
            if ( c0 && c1 && c2 ) {
                resultTris[idxCursor++] = tri;
            }else { // At least one vertex is invisible and at least one is visible
                var resultIsSingleTriangle : Bool;
                var vSwap;
                if ( c0 ) {
                    if ( !c1 && !c2 ) {
                        // v0 is visible, v1 and v2 are not
                        // no reorder needed
                        resultIsSingleTriangle = true;
                    }else if ( c2 ) {
                        // v0 and v2 are visible, v1 is not
                        vSwap = v0; v0 = v1; v1 = v2; v2 = vSwap;
                        resultIsSingleTriangle = false;
                    }else {
                        // v0 and v1 are visible, v2 is not
                        vSwap = v2; v2 = v1; v1 = v0; v0 = vSwap;
                        resultIsSingleTriangle = false;
                    }
                }else {
                    if ( c1 && c2 ) {
                        // v1 and v2 are visible, v0 is not
                        // no reorder needed
                        resultIsSingleTriangle = false;
                    }else if ( c1 ) {
                        // v1 is visible, v0 and v2 are not
                        vSwap = v0; v0 = v1; v1 = v2; v2 = vSwap;
                        resultIsSingleTriangle = true;
                    }else {
                        // v2 is visible, v0 and v1 are not
                        vSwap = v2; v2 = v1; v1 = v0; v0 = vSwap;
                        resultIsSingleTriangle = true;
                    }
                }
                
                
                var newVertsIdx = resultVerts.length;
                resultVerts.length = newVertsIdx + 2; // Add two new vertices;

                var f0 : Float = v0.screenZ;
                var f1 : Float = v1.screenZ;
                var f2 : Float = v2.screenZ;
                
                var factor = -f1 / (f0 - f1);
                resultVerts[newVertsIdx] = new BspVertex(   newVertsIdx,
                                                            v1.pos.clone().interpolate( v0.pos, factor ),
                                                            v1.uv.clone().interpolate( v0.uv, factor ),
                                                            v1.lmuv.clone().interpolate( v0.lmuv, factor) );

                factor = -f2 / (f0 - f2);

                resultVerts[newVertsIdx + 1] = new BspVertex(   newVertsIdx + 1,
                                                                v2.pos.clone().interpolate( v0.pos, factor ),
                                                                v2.uv.clone().interpolate( v0.uv, factor ),
                                                                v2.lmuv.clone().interpolate( v0.lmuv, factor) );
                
                if ( resultIsSingleTriangle ) {
                    var ntri = new BspTriangle();
                    ntri.v0 = v0;
                    ntri.v1 = resultVerts[newVertsIdx];
                    ntri.v2 = resultVerts[newVertsIdx + 1];
                    resultTris[idxCursor++] = ntri;
                }else {
                    var ntri = new BspTriangle();
                    ntri.v0 = resultVerts[newVertsIdx];
                    ntri.v1 = v1;
                    ntri.v2 = v2;
                    resultTris[idxCursor++] = ntri;
                    
                    ntri = new BspTriangle();
                    ntri.v0 = resultVerts[newVertsIdx];
                    ntri.v1 = v2;
                    ntri.v2 = resultVerts[newVertsIdx+1];
                    resultTris[idxCursor++] = ntri;
                }
                
            }
            
        }
        
        verts = resultVerts;
        tris = resultTris;
        numTris = resultTris.length;
        
        testAsserts();
    }

}